// Listing C19:19.7-- static data member and function in template
#include <iostream>
#include <ostream>
using namespace std;

const int DefaultSize = 3;

// A trival class for adding to arrays
class Animal
{
    public:
        Animal(int);
        Animal();
        ~Animal();

        int GetWeight() const { return itsWeight; }
        void SetWeight(int theWeight) { itsWeight = theWeight; }

        friend ostream&
        operator<<(ostream&, const Animal&);
    private:
        int itsWeight;
};

// extraction operator for printing animals
ostream& operator<<(ostream& theStream, const Animal& theAnimal)
{
    theStream << theAnimal.GetWeight();
    return theStream;
}

Animal::Animal(int weight)
: itsWeight(weight)
{
    // cout << "animal(int) ";
}

Animal::Animal()
: itsWeight(0)
{
    // cout << "animal()" << endl;
}

Animal::~Animal()
{
    // cout << "Destroyed an animal...";
}

template<class T>
class Array
{
    public:
        Array(int itsSize = DefaultSize);
        Array(const Array& rhs);
        ~Array() { delete [] pType; itsNumberArrays--; }

        // operators;
        Array& operator=(const Array&);
        T& operator[](int offSet) { return pType[offSet]; }
        const T& operator[](int offSet) const
        {
            return pType[offSet];
        }

        int GetSize() const { return itsSize; }
        static int GetNumberArrays() { return itsNumberArrays; }
        friend ostream& operator<<(ostream&, const Array<T>&);
    private:
        T *pType;
        int itsSize;
        static int itsNumberArrays;
};

template<class T>
int Array<T>::itsNumberArrays = 0;

template<class T>
Array<T>::Array(int size = DefaultSize)
: itsSize(size)
{
    pType = new T[size];
    for (int i = 0; i < size; i++)
    {
        pType[i] = (T)0;
    }

    itsNumberArrays++;
}

template<class T>
Array<T>& Array<T>::operator=(const Array& rhs)
{
    if (this == rhs)
    {
        return *this;
    }

    delete [] pType;
    itsSize = rhs.GetSize();
    pType = new T[itsSize];
    for (int i = 0; i < itsSize; i++)
    {
        pType[i] = rhs[i];
    }
}
template<class T>
Array<T>::Array(const Array& rhs)
{
    itsSize = rhs.GetSize();
    pType = new T[itsSize];
    for (int i = 0; i < itsSize; i++)
    {
        pType[i] = rhs[i];
    }

    itsNumberArrays++;
}

template<class T>
ostream& operator<<(ostream& output, const Array<T>& theArray)
{
    for (int i = 0; i < theArray.GetSize(); i++)
    {
        output << "[" << i << "] " << theArray[i] << endl;
    }

    return output;
}

int main(void)
{
    cout << Array<int>::GetNumberArrays() << " integer arrays\n";
    cout << Array<Animal>::GetNumberArrays();
    cout << " animal arrays " << endl << endl;
    Array<int> intArray;
    Array<Animal> animalArray;
    cout << intArray.GetNumberArrays() << " integer arrays\n";
    cout << animalArray.GetNumberArrays();
    cout << " animal arrays " << endl << endl;

    Array<int> *pIntArray = new Array<int>;
    cout << Array<int>::GetNumberArrays() << " integer arrays\n";
    cout << Array<Animal>::GetNumberArrays();
    cout << " animal arrays" << endl << endl;

    delete pIntArray;
    cout << Array<int>::GetNumberArrays() << " integer arrays\n";
    cout << Array<Animal>::GetNumberArrays();
    cout << " animal arrays" << endl << endl;
}


